using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace WbsEditor.Generation
{
    public partial class IterationWorkloadChartDialog : Form
    {
        #region Constructors / Initialization

        public IterationWorkloadChartDialog(WorkPackageInfo[] infos, WbsCanvas baseCanvas, WbsGenerator generator, string[] iterations)
        {
            _infos = infos;
            _baseCanvas = baseCanvas;
            _generator = generator;
            _iterations = iterations;
            InitializeComponent();
        }
        
        protected override void OnLoad(EventArgs e)
        {
            InitCharts();

            base.OnLoad(e);
        }

        #endregion

        #region Private Fields

        private WorkPackageInfo[] _infos;
        private WbsCanvas _baseCanvas;
        private WbsGenerator _generator;
        private string[] _iterations;
        private DataPoint _lastHighlightedDataPoint;
        private DataPointInfo _clickedInfo;
        
        #endregion

        #region Chart Creation
        
        private void InitCharts()
        {
            // Extract workload data on the fly.
            _generator.GenerateWorkloadData(_infos, _iterations);

            InitPeople();
            InitTasks();
            InitCompletedTasks();
            InitTotalTasks();
            InitSums(_alignScalingCheck.Checked);
        }

        private void InitSums(bool alignScaling)
        {
            double remainingTotal = 0.0;
            double completedTotal = 0.0;
            double maximumTotal = 0.0;
            foreach (WorkPackageInfo info in _infos)
            {
                if (info.Type == WorkPackageInfo.WorkPackageType.WorkBreakdownStructure)
                {
                    info.WorkBreakdownStructure.RecalculateTotalWork();
                    double remaining = info.WorkBreakdownStructure.Root.InnerNodeRemainingWorkInDays;
                    double completed = info.WorkBreakdownStructure.Root.InnerNodeCompletedWorkInDays;
                    remainingTotal += remaining;
                    completedTotal += completed;
                    maximumTotal = Math.Max(maximumTotal, remaining + completed);
                }
                else if (info.Type == WorkPackageInfo.WorkPackageType.OrphanTasks)
                {
                    foreach (WbsNode orphan in info.WorkItems)
                    {
                        remainingTotal += orphan.RemainingWorkInDays;
                        completedTotal += orphan.CompletedWorkInDays;
                    }
                }
            }
            double total = remainingTotal + completedTotal;

            _remainingLabel.Text = string.Format("{0:F2} PD", remainingTotal);
            _completedLabel.Text = string.Format("{0:F2} PD", completedTotal);
            _totalLabel.Text = string.Format("{0:F2} PD", total);

            if (alignScaling)
            {
                double roundedMax = Math.Ceiling(maximumTotal / 10.0) * 10.0;
                _totalChart.ChartAreas[0].AxisY.Maximum = roundedMax;
                _workItemChart.ChartAreas[0].AxisY.Maximum = roundedMax;
                _completedChart.ChartAreas[0].AxisY.Maximum = roundedMax;
            }
            else
            {
                _totalChart.ChartAreas[0].AxisY.Maximum = double.NaN;
                _workItemChart.ChartAreas[0].AxisY.Maximum = double.NaN;
                _completedChart.ChartAreas[0].AxisY.Maximum = double.NaN;
            }
        }

        private void InitTasks()
        {
            InitWbsChart(_workItemChart, WbsChartMode.RemainingWork);
        }

        private void InitCompletedTasks()
        {
            InitWbsChart(_completedChart, WbsChartMode.CompletedWork);
        }

        private void InitTotalTasks()
        {
            InitWbsChart(_totalChart, WbsChartMode.TotalWork);
        }

        private enum WbsChartMode
        {
            RemainingWork,
            CompletedWork,
            TotalWork
        }

        private void InitWbsChart(Chart chart, WbsChartMode mode)
        {
            chart.Series.Clear();

            var data = _infos.SelectMany(info => info.WorkloadInfo.Select(
                p => new { WorkItemName = GetName(info), Person = p.Name, Remaining = p.RemainingWork, Completed = p.CompletedWork, Total = p.RemainingWork + p.CompletedWork, Info = info })).ToArray();
            var workItems = data.Select(w => w.WorkItemName).Distinct().OrderBy(w => w);
            var persons = data.GroupBy(w => w.Person).OrderByDescending(w => w.Key);

            foreach (var person in persons)
            {
                Series series = this.CreateSeries(person.Key);

                var lookup = person.ToLookup(a => a.WorkItemName);
                foreach (var workItem in workItems)
                {
                    string axis = workItem;
                    string tooltip = person.Key;
                    float value = 0f;
                    switch (mode)
                    {
                    case WbsChartMode.RemainingWork: 
                        value = lookup[workItem].Select(w => w.Remaining).FirstOrDefault();
                        break;
                    case WbsChartMode.CompletedWork:
                        value = lookup[workItem].Select(w => w.Completed).FirstOrDefault();
                        break;
                    case WbsChartMode.TotalWork:
                        value = lookup[workItem].Select(w => w.Total).FirstOrDefault();
                        break;
                    } 
                    WorkPackageInfo info = lookup[workItem].Select(w => w.Info).FirstOrDefault();

                    DataPoint point = this.CreateDatapoint(axis, tooltip, value, new DataPointInfo(info, person.Key));
                    series.Points.Add(point);
                }

                chart.Series.Add(series);
            }

            Series sums = CreateSeries("Sums");
            sums.IsVisibleInLegend = false;
            foreach (WorkPackageInfo info in _infos.OrderBy(i => GetName(i)))
            {
                if (info.Type == WorkPackageInfo.WorkPackageType.WorkBreakdownStructure)
                {
                    float value = 0f;
                    switch (mode)
                    {
                        case WbsChartMode.RemainingWork:
                            value = info.WorkBreakdownStructure.Root.InnerNodeRemainingWorkInDays;
                            break;
                        case WbsChartMode.CompletedWork:
                            value = info.WorkBreakdownStructure.Root.InnerNodeCompletedWorkInDays;
                            break;
                        case WbsChartMode.TotalWork:
                            value = info.WorkBreakdownStructure.Root.InnerNodeRemainingWorkInDays +
                                info.WorkBreakdownStructure.Root.InnerNodeCompletedWorkInDays;
                            break;
                    }

                    DataPoint point = CreateDatapoint(GetName(info), value.ToString("0.##"), 5f, new DataPointInfo(info, "Sums"));
                    sums.Points.Add(point);
                }
            }
            chart.Series.Add(sums);
        }
        private void InitPeople()
        {
            this._peopleChart.Series.Clear();

            var data = _infos.SelectMany(info => info.WorkloadInfo.Select(p => new { WorkItemName = GetName(info), Person = p.Name, Remaining = p.RemainingWork, Info = info })).ToArray();
            var persons = data.Select(w => w.Person).Distinct().OrderBy(w => w); ;
            var workItems = data.GroupBy(w => w.WorkItemName).OrderBy(w => w.Key);

            foreach (var item in workItems.OrderBy(w => w.Key))
            {
                Series series = this.CreateSeries(item.Key);

                var lookup = item.ToLookup(a => a.Person);
                foreach (var person in persons)
                {
                    string axis = person;
                    string tooltip = item.Key;
                    float value = lookup[person].Select(w => w.Remaining).FirstOrDefault();
                    WorkPackageInfo info = lookup[person].Select(w => w.Info).FirstOrDefault();

                    DataPoint point = this.CreateDatapoint(axis, tooltip, value, new DataPointInfo(info, person));
                    series.Points.Add(point);
                }

                this._peopleChart.Series.Add(series);
            }
        }

        private Series CreateSeries(string item)
        {
            Series series = new Series(item);

            series.ChartType = SeriesChartType.StackedColumn;

            return series;
        }
        private DataPoint CreateDatapoint(string axis, string tooltip, float value, DataPointInfo info)
        {
            DataPoint result = new DataPoint();

            result.SetValueY(value);
            result.AxisLabel = axis;
            bool isSum = info.Person.Equals("Sums");
            if (isSum)
                result.Label = tooltip;
            else if (value > 0.001f)
                result.Label = string.Format("{0:0.##}", value);
            else
                result.Label = "";
            result.LabelToolTip = result.ToolTip = tooltip;
            if (isSum)
            {
                result.Color = Color.Transparent;
                result.LabelBackColor = Color.White;
                result.LabelForeColor = Color.Black;
                result.LabelBorderColor = Color.Black;
                result.LabelBorderWidth = 1;
            }
            result.Tag = info;

            return result;
        }
        private string GetName(WorkPackageInfo info)
        {
            if (info.Type == WorkPackageInfo.WorkPackageType.Bugs)
                return "Bugs in iteration";
            if (info.Type == WorkPackageInfo.WorkPackageType.OrphanTasks)
                return "Orphan Tasks\nin iteration";
            
            if (info.WorkBreakdownStructure == null)
                return String.Empty;
            string titleString = null;
            if (String.IsNullOrEmpty(info.WorkBreakdownStructure.TID))
                titleString = String.Format("{0} ({1})", info.WorkBreakdownStructure.Root.Title, GetStrippedIteration(info.WorkBreakdownStructure.Root.Iteration));
            else
                titleString = String.Format("{0} - {1} ({2})", info.WorkBreakdownStructure.TID, info.WorkBreakdownStructure.Root.Title, GetStrippedIteration(info.WorkBreakdownStructure.Root.Iteration));
            return WrapAround(titleString);
        }

        private object GetStrippedIteration(string p)
        {
            int lastIndex = p.LastIndexOf('\\');
            if (lastIndex >= 0)
                return p.Substring(lastIndex + 1);
            return p;
        }

        private static string WrapAround(string titleString)
        {
            const int threshold = 30;
            int len = titleString.Length;
            if (len <= threshold)
                return titleString;
            int spacePos = titleString.IndexOf(' ', len/2 - 4);
            if (spacePos < 0 || (spacePos - 15) > (len / 2))
            {
                // Force a break
                string partOne = titleString.Substring(0, len/2);
                string partTwo = titleString.Substring(len/2);
                return String.Format("{0}-\n{1}", partOne, partTwo);
            }
            else
            {
                // Break at space
                string partOne = titleString.Substring(0, spacePos);
                string partTwo = titleString.Substring(spacePos + 1);
                return String.Format("{0}\n{1}", partOne, partTwo);
            }
        }

        
        #endregion

        #region Chart Interaction

        private void _chart_MouseMove(object sender, MouseEventArgs e)
        {
            Chart chart = (Chart)sender;
            HitTestResult result = chart.HitTest(e.X, e.Y);

            if (null != _lastHighlightedDataPoint)
            {
                _lastHighlightedDataPoint.BackHatchStyle = ChartHatchStyle.None;
            }

            if (result.ChartElementType == ChartElementType.DataPoint ||
                result.ChartElementType == ChartElementType.DataPointLabel)
            {
                DataPoint dp = result.Series.Points[result.PointIndex];
                DataPointInfo info = (DataPointInfo)dp.Tag;
                if (null != info)
                {
                    this.Cursor = Cursors.Hand;
                    dp.BackHatchStyle = ChartHatchStyle.Percent25;
                    _lastHighlightedDataPoint = dp;
                }
            }
            else
            {
                this.Cursor = Cursors.Default;
                _lastHighlightedDataPoint = null;
            }
        }
        private void _chart_MouseDown(object sender, MouseEventArgs e)
        {
            if (null == _lastHighlightedDataPoint)
                return;
            DataPointInfo info = (DataPointInfo)_lastHighlightedDataPoint.Tag;
            if (null == info)
                return;

            if (e.Button == MouseButtons.Left)
            {
                if (info.Info.Type == WorkPackageInfo.WorkPackageType.WorkBreakdownStructure)
                {
                    DisplayWbsDialog(info);
                }
                else
                {
                    DisplayPersonalizedWbsDialog(info.Person);
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                _clickedInfo = info;
                if (info.Info.Type == WorkPackageInfo.WorkPackageType.WorkBreakdownStructure)
                {
                    _openWbsToolStripMenuItem.Visible = true;
                }
                else
                {
                    _openWbsToolStripMenuItem.Visible = false;
                }
                _dataPointMenu.Show(this, e.X, e.Y);
            }
        }

        private void _openWbsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == _clickedInfo)
                return;
            DisplayWbsDialog(_clickedInfo);
        }
        private void _openPersonalizedWbsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == _clickedInfo)
                return;
            DisplayPersonalizedWbsDialog(_clickedInfo.Person);
        }

        private void DisplayPersonalizedWbsDialog(string name)
        {
            TempWbsDisplayDialog wbsDialog = new TempWbsDisplayDialog();
            WbsSettings settings = _baseCanvas.Settings.Clone();
            settings.HighlightName = "";
            settings.CheckPlausis = false;
            settings.UseWbsCodes = false;
            wbsDialog.WbsCanvas.Settings = settings;
            wbsDialog.WbsCanvas.TfsClient = _baseCanvas.TfsClient;
            if (name.Equals("(unassigned)", StringComparison.CurrentCultureIgnoreCase))
            {
                name = "";
            }
            try
            {
                WorkBreakdownStructure wbs = _generator.GeneratePersonalizedWbsFromWorkPackageInfos(_infos, name);
                wbsDialog.WbsCanvas.SetWorkBreakdownStructure(wbs);
                wbsDialog.WbsCanvas.ReadOnly = true;
                wbsDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Unhandled (but caught) exception:\n\n" + ex.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Re-Init chart, values may have changed.
            InitPeople();
        }
        private void DisplayWbsDialog(DataPointInfo info)
        {
            TempWbsDisplayDialog wbsDialog = new TempWbsDisplayDialog();
            WbsSettings settings = _baseCanvas.Settings.Clone();
            wbsDialog.WbsCanvas.Settings = settings;
            wbsDialog.WbsCanvas.TfsClient = _baseCanvas.TfsClient;
            settings.HighlightName = info.Person;
            try
            {
                wbsDialog.WbsCanvas.SetWorkBreakdownStructure(info.Info.WorkBreakdownStructure);
                wbsDialog.WbsCanvas.ReadOnly = true;
                wbsDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Unhandled (but caught) exception:\n\n" + ex.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Re-Init chart, values may have changed.
            InitCharts();
        }

        #endregion

        #region Printing

        private void _printButton_Click(object sender, EventArgs e)
        {
            Chart chart;
            if (this.tabControl1.SelectedIndex == 0)
                chart = this._peopleChart;
            else if (this.tabControl1.SelectedIndex == 1)
                chart = this._workItemChart;
            else if (this.tabControl1.SelectedIndex == 2)
                chart = this._completedChart;
            else
                chart = this._totalChart;
            chart.Printing.PrintDocument.DefaultPageSettings.Landscape = true;
            chart.Printing.Print(true);
        }

        #endregion

        private void _alignScalingCheck_CheckedChanged(object sender, EventArgs e)
        {
            InitSums(_alignScalingCheck.Checked);
        }
    }

    internal sealed class DataPointInfo
    {
        public WorkPackageInfo Info
        {
            get;
            set;
        }
        public string Person
        {
            get;
            set;
        }

        public DataPointInfo(WorkPackageInfo info, string person)
        {
            this.Info = info;
            this.Person = person;
        }
    }
}